home *** CD-ROM | disk | FTP | other *** search
/ The Best of MacTutor - S…e Code for Volumes 1 to 5 / The Best of MacTutor - Source Code for Volume 1-5 (Wayzata Technology)(6031)(1990).bin / Source Code / #50 (Nov 89) / Inside Code / Meter.c next >
Text File  |  1989-03-16  |  43KB  |  1,510 lines

  1. /* Meter.c - display, drive and control a meter with set point.
  2. Useage: add 'Meter.c' to your project, and #include "Meter.h" in any
  3.     file that refers to the functions below. Add 'Math' and 
  4.     #include <math.h> if you don't hand-load the sineTable[].
  5. - the main purpose here is to show how the Control Manager works,
  6.     so we'll use functional equivalents of most of the Control Manager
  7.     routines listed in Inside Macintosh vol 1 pages 309 - 338 rather than
  8.     creating a new kind of control. In general, "Control" in IM is replaced
  9.     by "Meter" here; for example, if you're interested in
  10.     how NewControl() works see NewMeter() below. See MeterProc() in MeterMain.c
  11.     for an action procedure that is called by TrackMeter() to drive the 
  12.     set point indicator up and down. Fancy stuff requiring external user-defined
  13.     resources is not included (no GetNewMeter()), nor is a default action Proc
  14.     supported (always call TrackMeter() with your action Proc or 0L). Two other
  15.     differences that no one should mind - all functions are 'C' rather than 'pascal',
  16.     and all value parameters are long.
  17. - meterValue refers to the set point value, which is normally under user control,
  18.     and can be set by SetMtrValue().
  19. - needleValue refers to current input to the meter, displayed by the needle
  20.     and NOT normally under user's control, set by SetNeedleValue().
  21. - the digital display normally shows needleValue unless mouse is down in
  22.     meter background (simple equivalent of control "thumb").
  23. -    for a more detailed look at controlling what happens when the mouse is
  24.     in an indicator see ThumbControl() in MeterMain.c.
  25. -    the arrow and paging parts appear as small and large triangles (defined as
  26.     polygons), with the standard part codes.
  27. - meter handles are linked to the owning windows' refCon field. If you want to
  28.     use this field for something else, change the refCon references below in NewMeter(),
  29.     DisposeMeter(), KillMeters(), DrawMeters(), and FindMeter().
  30. */
  31.  
  32. /* Do not include Meter.h, but DO #include <MacHeaders> either 
  33. explicitly here or by setting the "<MacHeaders>" option  */
  34.  
  35. /* math.h is needed for the sineTable[] array, otherwise hand-load it.  */
  36. #define _ERRORCHECK_
  37. int errno;
  38. #include <math.h> 
  39.  
  40. /* special partCode for meter, everything but the four up and down arrows.
  41. A meter has no true thumb - the set point indicator is a skinny rotating
  42. target, and too dangerous to grab if the meter is being used for closed-
  43. loop control. "Track and hilite the background" just means show the set
  44. point value in the display rather than the current needle value while the
  45. mouse is down.*/
  46. #define        inBackground    24
  47.  
  48. #define    NULL            0L
  49. #define    HASMETER        'qwer'    /* arbitrary long ID, used by ValidMeter() */
  50.  
  51. /* Some #defines to vary the appearance of the meter. Since meters are
  52. complicated beasts to draw, be prepared to fine-tune the drawing code
  53. as well if you explore different looks! */
  54. #define    MINWIDTH        48        /* min. usable meter width */
  55. #define TVHRATIO        1400L    /* black rounded rect height/width, times 1000 */
  56. #define TITLEHEIGHT          24        /* height of title box below meter proper, plus 4 */
  57. /* given width of meter = w, total height is (w*TVHRATIO)/1000 + TITLEHEIGHT */
  58. #define    TEXTVOFFSET           4        /* to centre 12 pt text in the display box */
  59. #define INNERPCT         94L        /* used for defining needle and set point
  60.     lengths as a % of dial radius */
  61. #define    HALFSETWIDTH       1        /* half width of set point indicator */
  62. #define    CENTRERADIUS       3        /* radius of meter's little centre circle */
  63. #define    PGPOLYHEIGHT    3000L    /* times 1000, keep it between 1000 and 3000 */
  64. #define    INCPOLYHEIGHT    2000L
  65. /* --POLYHEIGHT is ratio of height to width of set point control
  66.     triangles times 1000 - try 1000,1414,1732,2000,3000. */
  67. #define    NUMTICKS        11        /* number of major position ticks around circumference */
  68. #define    NUMMINORTICKS    51        /* NUMMINORTICKS includes the NUMTICKS. */
  69. #define    HALFNEEDLERATIO    8        /*  dial radius/HALFNEEDLERATIO = halfNeedleWidth */
  70. #define    MINDIGITS        3        /* minimum number of digits to display */
  71. #define    MAXDIGITS        5        /* maximum number of digits to display */
  72. /* The digital display will show at least MINDIGITS for small meters, and at most
  73.     MAXDIGITS for large meters. See ShowValue(), AdjustDisplay(), and SetDisplayDivisor()
  74.     for details. As a nicety, you should update the meter title to reflect the actual
  75.     units displayed (eg if input is Volts and displayDivisor is 10, the meter name should
  76.     be something like "Volts / 10" */
  77.  
  78. static int sineTable[91]; /* set up by InitMeters() */
  79.  
  80. struct MeterRecord
  81.     {
  82.     struct MeterRecord        **nextMeter; /* or NULL */
  83.     WindowPtr        meterOwner;
  84.     Rect            meterRect; /* includes title */
  85.     Byte            meterVis;
  86.     Byte            meterHilite;
  87.     long            meterValue, meterMin, meterMax, needleValue;
  88.     /*ProcPtr            meterAction; - not implemented */
  89.     long            meterRfCon;
  90.     char            title[32]; /* pascal format, length byte first */
  91.     /* some additional fields not found in a control record */
  92.     Rect            incDownRect, incUpRect, pageDownRect, pageUpRect;
  93.     Rect            displayRect; /* digital display */
  94.     long            displayDivisor; /* power of ten */
  95.     int                needleShort, needleLong, halfNeedleWidth;
  96.     int                setShort, setLong, halfSetWidth;
  97.     PicHandle        backgroundH;
  98.     PolyHandle        incUpH, incDownH, pageUpH, pageDownH; /* triangles */
  99.     Point            dialCentre;
  100.     long            hasMeterID; /* = HASMETER for a meter */
  101.     };
  102. typedef struct MeterRecord MeterRecord;
  103. typedef MeterRecord *MeterPtr;
  104. typedef MeterRecord **MeterHandle;
  105.  
  106. typedef void (*voidPtr)(); /* true C equiv. of procPtr */
  107.  
  108. /* Functions defined in this file */
  109.  
  110. /* external interface, mostly like Control Manager */
  111. /* Control Manager equivalents */
  112. MeterHandle    NewMeter(WindowPtr, Rect*, char*, Boolean, long, long, long, long);
  113. void        DisposeMeter(MeterHandle);
  114. void        KillMeters(WindowPtr);
  115. void        SetMTitle(MeterHandle, char*);
  116. void        GetMTitle(MeterHandle, char*);
  117. void        HideMeter(MeterHandle);
  118. void        ShowMeter(MeterHandle);
  119. void        DrawMeters(WindowPtr);
  120. void        HiliteMeter(MeterHandle, int);
  121. int            FindMeter(Point, WindowPtr, MeterHandle*);
  122. int            TrackMeter(MeterHandle, Point, voidPtr);
  123. int            TestMeter(MeterHandle, Point);
  124. void        MoveMeter(MeterHandle, int, int);
  125. void        DragMeter(MeterHandle, Point, Rect*, Rect*, int);
  126. void        SizeMeter(MeterHandle, int); /* note proportions are fixed */
  127. void        SetMtrValue(MeterHandle, long); /* use this to move the set point */
  128. long        GetMtrValue(MeterHandle);
  129. void        SetMtrMin(MeterHandle, long);
  130. long        GetMtrMin(MeterHandle);
  131. void        SetMtrMax(MeterHandle, long);
  132. long        GetMtrMax(MeterHandle);
  133. void        SetMRefCon(MeterHandle, long);
  134. long        GetMRefCon(MeterHandle);
  135. /* Special meter functions */
  136. void        SetNeedleValue(MeterHandle, long); /* use this to move the needle */
  137. long        GetNeedleValue(MeterHandle);
  138. long        GetDisplayDivisor(MeterHandle);
  139. Boolean        ValidMeter(MeterHandle);
  140. int            GetMaxTitlePixels(MeterHandle); /* displayable title width */
  141. void        MeterSnapshot(MeterHandle); /* draws meter to thePort
  142.     rather than meter's owning window */
  143.  
  144. /* support functions used in this file only */
  145. void        InitMeters(void);
  146. void        CreateMeterBackground(MeterHandle);
  147. void        DrawMeterBackground(MeterHandle);
  148. void        DrawMeterTitle(MeterHandle);
  149. void        DrawTriangle(MeterHandle, int);
  150. void        DrawNeedle(MeterHandle);
  151. void        DrawSet(MeterHandle);
  152. void        ShowValue(MeterHandle, Boolean);
  153. void        DrawInactiveMeter(MeterHandle);
  154. void        AdjustDisplay(MeterHandle);
  155. void        SetDisplayDivisor(MeterHandle);
  156. void        GetSinAndCos(int, long*, long*);
  157.  
  158. /* New meter handles are linked to the window's refCon field. Meter proportions are fixed;
  159. height is determined from width, and boundRect.bottom is ignored. There is no default
  160. action procedure. Titles are truncated on the right.*/
  161. MeterHandle    NewMeter(wPtr, boundRect, title, visible, meterValue, meterMin, meterMax, meterRfCon)
  162. WindowPtr        wPtr;
  163. Rect            *boundRect;
  164. char            *title;
  165. Boolean            visible;
  166. long            meterValue, meterMin, meterMax;
  167. long            meterRfCon;
  168.     {
  169.     int                i,j;
  170.     MeterHandle        mH, tempMH;
  171.     MeterPtr        mPtr;
  172.     WindowPeek        wPk = (WindowPeek)wPtr;
  173.     char            *titlePtr;
  174.     
  175.     /* Initialize sine table if necessary. */
  176.     if (sineTable[45] == 0) /* should be 707 by the way */
  177.         InitMeters();
  178.     /* Allocate and attach a new meter record. */
  179.     mH = (MeterHandle)NewHandle(sizeof(MeterRecord));
  180.     if (MemError() != noErr)
  181.         return(NULL);
  182.     mPtr = *mH;
  183.     mPtr->meterOwner = wPtr;
  184.     mPtr->nextMeter = NULL;
  185.     mPtr->hasMeterID = HASMETER;
  186.     /* refCon of WindowRecord holds first MeterHandle. */
  187.     /* New meters are inserted at beginning of list. */
  188.     tempMH = (MeterHandle)(wPk->refCon);
  189.     wPk->refCon = (long)mH;
  190.     if (tempMH && ((**tempMH).hasMeterID == HASMETER))
  191.         mPtr->nextMeter = tempMH;
  192.     /* Fill in the easy fields. */
  193.     mPtr->meterVis = visible;
  194.     mPtr->meterHilite = 0; /* active */
  195.     /* meterMin <= meterValue <= meterMax */
  196.     if (meterMax < meterMin)
  197.         {
  198.         DisposeMeter(mH);
  199.         return(NULL);
  200.         }
  201.     if (meterValue < meterMin)
  202.         meterValue = meterMin;
  203.     if (meterValue > meterMax)
  204.         meterValue = meterMax;
  205.     if (meterMin == meterMax)
  206.         (**mH).meterHilite = 255; /* inactive - see IM 1-327 */
  207.     mPtr->meterValue = meterValue;
  208.     mPtr->meterMin = meterMin;
  209.     mPtr->meterMax = meterMax;
  210.     mPtr->meterRfCon = meterRfCon;
  211.     mPtr->needleValue = meterMin; /* default value */
  212.     j = title[0];
  213.     if (j > 31)
  214.         j = 31;
  215.     titlePtr = mPtr->title;
  216.     for (i = 0; i <= j; ++i)
  217.         *titlePtr++ = *title++;
  218.     (**mH).meterRect = *boundRect;
  219.     (**mH).halfSetWidth = HALFSETWIDTH;
  220.     /* Do the grunt work. */
  221.     CreateMeterBackground(mH);
  222.     /* Draw meter if visible. */
  223.     if (visible)
  224.         {
  225.         (**mH).meterVis = FALSE; /* forces drawing */
  226.         ShowMeter(mH);
  227.         }
  228.     return(mH);
  229.     } /* end NewMeter() */
  230.  
  231. void        DisposeMeter(mH)
  232. MeterHandle        mH;
  233.     {
  234.     Rect        tempRect;
  235.     PicHandle    tempPH;
  236.     PolyHandle    tempPoly;
  237.     MeterHandle    tMH;
  238.     GrafPtr        savePort;
  239.     
  240.     if (!ValidMeter(mH)) return;
  241.     GetPort(&savePort);
  242.     SetPort((**mH).meterOwner);
  243.     tempPH = (**mH).backgroundH;
  244.     KillPicture(tempPH);
  245.     tempPoly = (**mH).incUpH;
  246.     KillPoly(tempPoly);
  247.     tempPoly = (**mH).incDownH;
  248.     KillPoly(tempPoly);
  249.     tempPoly = (**mH).pageUpH;
  250.     KillPoly(tempPoly);
  251.     tempPoly = (**mH).pageDownH;
  252.     KillPoly(tempPoly);
  253.     tempRect = (**mH).meterRect;
  254.     EraseRect(&tempRect);
  255.     InvalRect(&tempRect);
  256.     /* Unlink the meter. */
  257.     tMH = (MeterHandle)(((WindowPeek)((**mH).meterOwner))->refCon);
  258.     if (tMH != mH)
  259.         {
  260.         while ((**tMH).nextMeter != mH)
  261.             tMH = (**tMH).nextMeter;
  262.         (**tMH).nextMeter = (**mH).nextMeter;
  263.         }
  264.     else
  265.         ((WindowPeek)((**mH).meterOwner))->refCon = (long)((**mH).nextMeter);
  266.     DisposHandle((Handle)mH);
  267.     SetPort(savePort);
  268.     } /* end DisposeMeter() */
  269.  
  270. void        KillMeters(wPtr)
  271. WindowPtr        wPtr;
  272.     {
  273.     WindowPeek    wPk = (WindowPeek)wPtr;
  274.  
  275.     while (wPk->refCon && ((**((MeterHandle)(wPk->refCon))).hasMeterID == HASMETER))
  276.         DisposeMeter((MeterHandle)(wPk->refCon));
  277.     } /* end KillMeters() */
  278.  
  279. void        SetMTitle(mH, title)
  280. MeterHandle        mH;
  281. char            *title;
  282.     {
  283.     int j, i = title[0];
  284.     GrafPtr        savePort;
  285.     Rect        tempRect;
  286.     
  287.     if (!ValidMeter(mH)) return;
  288.     if (i > 31)
  289.         i = 31;
  290.     if (i)
  291.         {
  292.         for (j = 0; j <= i; ++j)
  293.             (**mH).title[j] = title[j];
  294.         GetPort(&savePort);
  295.         SetPort((**mH).meterOwner);
  296.         DrawMeterTitle(mH);
  297.         SetPort(savePort);
  298.         }
  299.     } /* end SetMTitle() */
  300.  
  301. void        GetMTitle(mH, title)
  302. MeterHandle        mH;
  303. char            *title;
  304.     {
  305.     int j, i = (**mH).title[0];
  306.     if (!ValidMeter(mH))
  307.         {
  308.         title[0] = 0;
  309.         return;
  310.         }
  311.     for (j = 0; j <=i; ++j)
  312.         title[j] = (**mH).title[j];
  313.     } /* end GetMTitle() */
  314.  
  315. void        HideMeter(mH)
  316. MeterHandle        mH;
  317.     {
  318.     Rect        tempRect;
  319.     GrafPtr        savePort;
  320.     
  321.     if (!ValidMeter(mH)) return;
  322.     if (!(**mH).meterVis) return;
  323.     GetPort(&savePort);
  324.     SetPort((**mH).meterOwner);
  325.     (**mH).meterVis = FALSE;
  326.     tempRect = (**mH).meterRect;
  327.     EraseRect(&tempRect);
  328.     InvalRect(&tempRect);
  329.     SetPort(savePort);
  330.     } /* end HideMeter() */
  331.  
  332. void        ShowMeter(mH)
  333. MeterHandle        mH;
  334.     {
  335.     int        i;
  336.     GrafPtr        savePort;
  337.     
  338.     if (!ValidMeter(mH)) return;
  339.     if ((**mH).meterVis) return;
  340.     GetPort(&savePort);
  341.     SetPort((**mH).meterOwner);
  342.     (**mH).meterVis = TRUE;
  343.     DrawMeterBackground(mH);
  344.     for (i = inUpButton; i <= inPageDown; ++i)
  345.         DrawTriangle(mH, i);
  346.     DrawNeedle(mH);
  347.     DrawSet(mH);
  348.     ShowValue(mH, TRUE);
  349.     SetPort(savePort);
  350.     } /* end ShowMeter() */
  351.  
  352. void        DrawMeters(wPtr)
  353. WindowPtr        wPtr;
  354.     {
  355.     int            i;
  356.     GrafPtr        savePort;
  357.     MeterHandle    mH = (MeterHandle)(((WindowPeek)wPtr)->refCon);
  358.     
  359.     if (!ValidMeter(mH)) return;
  360.     GetPort(&savePort);
  361.     SetPort(wPtr);
  362.     while (mH)
  363.         {
  364.         if ((**mH).meterVis)
  365.             {
  366.             DrawMeterBackground(mH);
  367.             for (i = inUpButton; i <= inPageDown; ++i)
  368.                 DrawTriangle(mH, i);
  369.             DrawNeedle(mH);
  370.             DrawSet(mH);
  371.             ShowValue(mH, TRUE);
  372.             }
  373.         mH = (**mH).nextMeter;
  374.         }
  375.     SetPort(savePort);
  376.     } /* end DrawMeters() */
  377.  
  378. void        HiliteMeter(mH, hiliteState)
  379. MeterHandle        mH;
  380. int                hiliteState;
  381.     {
  382.     int            i;
  383.     GrafPtr        savePort;
  384.     
  385.     if (!ValidMeter(mH)) return;
  386.     if ((**mH).meterHilite == hiliteState) return;
  387.     if ((**mH).meterHilite == 255 && hiliteState != 0) return;
  388.     GetPort(&savePort);
  389.     SetPort((**mH).meterOwner);
  390.     if (inUpButton <= hiliteState && hiliteState <= inPageDown)
  391.         {
  392.         (**mH).meterHilite = hiliteState;
  393.         DrawTriangle(mH, hiliteState);
  394.         }
  395.     else if (hiliteState == inBackground)
  396.         { /* see #define at top of file */
  397.         (**mH).meterHilite = hiliteState;
  398.         ShowValue(mH, FALSE);
  399.         }
  400.     else if (hiliteState == 0) /* make normal */
  401.         {
  402.         if ((**mH).meterHilite == 255)
  403.             {
  404.             (**mH).meterHilite = 0;
  405.             DrawMeterBackground(mH);
  406.             for (i = inUpButton; i <= inPageDown; ++i)
  407.                 DrawTriangle(mH, i);
  408.             DrawNeedle(mH);
  409.             DrawSet(mH);
  410.             ShowValue(mH, TRUE);
  411.             }
  412.         else if (inUpButton <= (**mH).meterHilite && (**mH).meterHilite <= inPageDown)
  413.             {
  414.             hiliteState = (**mH).meterHilite;
  415.             (**mH).meterHilite = 0;
  416.             DrawTriangle(mH, hiliteState);
  417.             }
  418.         else if ((**mH).meterHilite == inBackground)
  419.             {
  420.             (**mH).meterHilite = 0;
  421.             ShowValue(mH, TRUE);
  422.             }
  423.         }
  424.     else if (hiliteState == 255) /* make inactive */
  425.         {
  426.         (**mH).meterHilite = hiliteState;
  427.         DrawMeterBackground(mH);
  428.         }
  429.     SetPort(savePort);
  430.     } /* end HiliteMeter() */
  431.  
  432. int            FindMeter(thePoint, wPtr, mHP)
  433. Point            thePoint;
  434. WindowPtr        wPtr;
  435. MeterHandle        *mHP;
  436.     {
  437.     int            partCode;
  438.     Boolean        foundIt = FALSE;
  439.     MeterHandle    mH = (MeterHandle)(((WindowPeek)wPtr)->refCon);
  440.     
  441.     while (ValidMeter(mH))
  442.         {
  443.         if (PtInRect(thePoint, &((**mH).meterRect)))
  444.             {
  445.             foundIt = TRUE;
  446.             break;
  447.             }
  448.         mH = (**mH).nextMeter;
  449.         }
  450.     if (foundIt && (partCode = TestMeter(mH, thePoint)))
  451.         {
  452.         *mHP = mH;
  453.         return(partCode);
  454.         }
  455.     *mHP = NULL;
  456.     return(0);
  457.     } /* end FindMeter() */
  458.  
  459. /* See ThumbControl() in MeterMain.c for an inside look at
  460. tracking a moving indicator. */
  461. int            TrackMeter(mH, startPoint, actionProc)
  462. MeterHandle        mH;
  463. Point            startPoint;
  464. voidPtr            actionProc;
  465.     {
  466.     int            partCode;
  467.     Point        theMouse;
  468.     Rect        partRect, slopRect;
  469.     Boolean        pausing = FALSE;
  470.     
  471.     partCode = TestMeter(mH, startPoint);
  472.     if (!partCode) return(0);
  473.     if (inUpButton <= partCode && partCode <= inPageDown)
  474.         {
  475.         /* hilite part and call actionProc while mouse down in part */
  476.         if (partCode == inUpButton)
  477.             partRect = (**mH).incUpRect;
  478.         else if (partCode == inDownButton)
  479.             partRect = (**mH).incDownRect;
  480.         else if (partCode == inPageUp)
  481.             partRect = (**mH).pageUpRect;
  482.         else if (partCode == inPageDown)
  483.             partRect = (**mH).pageDownRect;
  484.         slopRect = partRect;
  485.         InsetRect(&slopRect, -10, -10);
  486.         HiliteMeter(mH, partCode);
  487.         while (StillDown())
  488.             {
  489.             GetMouse(&theMouse);
  490.             if (PtInRect(theMouse, &slopRect))
  491.                 {
  492.                 if (pausing)
  493.                     {
  494.                     pausing = FALSE;
  495.                     HiliteMeter(mH, partCode);
  496.                     }
  497.                 if (actionProc)
  498.                     (*actionProc)(mH, partCode);
  499.                 }
  500.             else
  501.                 {
  502.                 if (!pausing)
  503.                     {
  504.                     pausing = TRUE;
  505.                     HiliteMeter(mH, 0);
  506.                     }
  507.                 }
  508.             }
  509.         if (!pausing)
  510.             HiliteMeter(mH, 0);
  511.         }
  512.     else if (partCode == inBackground)
  513.         {
  514.         ShowValue(mH, FALSE);
  515.         while (StillDown())
  516.             {
  517.             if (actionProc)
  518.                     (*actionProc)();
  519.             }
  520.         ShowValue(mH, TRUE);
  521.         }
  522.     else return(0);
  523.     
  524.     } /* end TrackMeter() */
  525.  
  526. int            TestMeter(mH, thePoint)
  527. MeterHandle        mH;
  528. Point            thePoint;
  529.     {
  530.     int            partCode;
  531.     
  532.     if (!ValidMeter(mH)) return(0);
  533.     if (PtInRect(thePoint, &((**mH).meterRect)))
  534.         {
  535.         if ((**mH).meterVis && ((**mH).meterHilite != 255))
  536.             {
  537.             if (PtInRect(thePoint, &((**mH).incUpRect)))
  538.                 partCode = inUpButton;
  539.             else if (PtInRect(thePoint, &((**mH).incDownRect)))
  540.                 partCode = inDownButton;
  541.             else if (PtInRect(thePoint, &((**mH).pageUpRect)))
  542.                 partCode = inPageUp;
  543.             else if (PtInRect(thePoint, &((**mH).pageDownRect)))
  544.                 partCode = inPageDown;
  545.             else
  546.                 partCode = inBackground;
  547.             return(partCode);
  548.             }
  549.         }
  550.     return(0);
  551.     } /* end TestMeter() */
  552.  
  553. void        MoveMeter(mH, h, v)
  554. MeterHandle        mH;
  555. int                h,v;
  556.     {
  557.     Boolean        wasVisible = (**mH).meterVis;
  558.     
  559.     if (!ValidMeter(mH)) return;
  560.     if (wasVisible)
  561.         HideMeter(mH);
  562.     OffsetRect(&((**mH).meterRect), h, v);
  563.     OffsetRect(&((**mH).incDownRect), h, v);
  564.     OffsetRect(&((**mH).incUpRect), h, v);
  565.     OffsetRect(&((**mH).pageDownRect), h, v);
  566.     OffsetRect(&((**mH).pageUpRect), h, v);
  567.     OffsetRect(&((**mH).displayRect), h, v);
  568.     (**mH).dialCentre.h += h;
  569.     (**mH).dialCentre.v += v;
  570.     if (wasVisible)
  571.         ShowMeter(mH);
  572.     } /* end MoveMeter() */
  573.  
  574. void        DragMeter(mH, startPoint, limitRect, slopRect, axis)
  575. MeterHandle        mH;
  576. Point            startPoint;
  577. Rect            *limitRect;
  578. Rect            *slopRect;
  579. int                axis;
  580.     {
  581.     long        posOffset;
  582.     int            h,v;
  583.     Rect        mRect, tRect;
  584.     GrafPtr        savePort;
  585.     RgnHandle    tempRgn;
  586.     
  587.     if (!ValidMeter(mH)) return;
  588.     GetPort(&savePort);
  589.     SetPort((**mH).meterOwner);
  590.     tempRgn = NewRgn();
  591.     mRect = (**mH).meterRect;
  592.     mRect.bottom -= TITLEHEIGHT;
  593.     tRect.left = (**mH).meterRect.left + 2;
  594.     tRect.right = (**mH).meterRect.right - 2;
  595.     tRect.bottom = (**mH).meterRect.bottom - 2;
  596.     tRect.top = tRect.bottom - TITLEHEIGHT + 4;
  597.     
  598.     OpenRgn();
  599.     FrameRoundRect(&mRect,mRect.right/10, mRect.bottom/10);
  600.     FrameRect(&tRect);
  601.     CloseRgn(tempRgn);
  602.     
  603.     posOffset = DragGrayRgn(tempRgn,startPoint,limitRect,slopRect,axis,0L);
  604.     DisposeRgn(tempRgn);
  605.     v = HiWord(posOffset);
  606.     h = LoWord(posOffset);
  607.     if (v != -32768 && h != -32768)
  608.         MoveMeter(mH, h, v);
  609.     SetPort(savePort);
  610.     } /* end DragMeter() */
  611.  
  612. /* Meter proportions are fixed - width determines height. */
  613. void        SizeMeter(mH, w)
  614. MeterHandle        mH;
  615. int                w;
  616.     {
  617.     PicHandle    tempPH;
  618.     PolyHandle    tempPoly;
  619.     Boolean        wasVisible = (**mH).meterVis;
  620.     
  621.     if (!ValidMeter(mH)) return;
  622.     if (wasVisible)
  623.         HideMeter(mH);
  624.     /* gut the meter and recreate it */
  625.     tempPH = (**mH).backgroundH;
  626.     KillPicture(tempPH);
  627.     tempPoly = (**mH).incUpH;
  628.     KillPoly(tempPoly);
  629.     tempPoly = (**mH).incDownH;
  630.     KillPoly(tempPoly);
  631.     tempPoly = (**mH).pageUpH;
  632.     KillPoly(tempPoly);
  633.     tempPoly = (**mH).pageDownH;
  634.     KillPoly(tempPoly);
  635.     (**mH).meterRect.right = (**mH).meterRect.left + w;
  636.     CreateMeterBackground(mH);
  637.     if (wasVisible)
  638.         ShowMeter(mH);
  639.     } /* end SizeMeter() */
  640.  
  641. void        SetMtrValue(mH, meterValue)
  642. MeterHandle        mH;
  643. long            meterValue;
  644.     {
  645.     GrafPtr        savePort;
  646.     
  647.     if (!ValidMeter(mH)) return;
  648.     GetPort(&savePort);
  649.     SetPort((**mH).meterOwner);
  650.     DrawSet(mH); /* erases old pointer */
  651.     /* pin value to range min:max */
  652.     if (meterValue < (**mH).meterMin)
  653.         meterValue = (**mH).meterMin;
  654.     else if (meterValue > (**mH).meterMax)
  655.         meterValue = (**mH).meterMax;
  656.     (**mH).meterValue = meterValue;
  657.     DrawSet(mH);
  658.     ShowValue(mH, FALSE);
  659.     SetPort(savePort);
  660.     } /* end SetMtrValue() */
  661.  
  662. long        GetMtrValue(mH)
  663. MeterHandle        mH;
  664.     {
  665.     
  666.     if (!ValidMeter(mH)) return(0L);
  667.     return((**mH).meterValue);
  668.     } /* end GetMtrValue() */
  669.  
  670. void        SetMtrMin(mH, minValue)
  671. MeterHandle        mH;
  672. long            minValue;
  673.     {
  674.     GrafPtr        savePort;
  675.     
  676.     if (!ValidMeter(mH)) return;
  677.     GetPort(&savePort);
  678.     SetPort((**mH).meterOwner);
  679.     DrawSet(mH);     /* erases old pointer */
  680.     DrawNeedle(mH); /* ditto needle */
  681.     if (minValue >= (**mH).meterMax)
  682.         {
  683.         (**mH).meterMin = (**mH).meterMax;
  684.         HiliteMeter(mH, 255); /* inactive - see IM 1-327 */
  685.         return;
  686.         }
  687.     (**mH).meterMin = minValue;
  688.     if ((**mH).meterValue < minValue)
  689.         (**mH).meterValue = minValue;
  690.     SetDisplayDivisor(mH);
  691.     DrawSet(mH);
  692.     DrawNeedle(mH);
  693.     ShowValue(mH, FALSE);
  694.     SetPort(savePort);
  695.     } /* end SetMtrMin() */
  696.  
  697. long        GetMtrMin(mH)
  698. MeterHandle        mH;
  699.     {
  700.     
  701.     if (!ValidMeter(mH)) return(0L);
  702.     return((**mH).meterMin);
  703.     } /* end GetMtrMin() */
  704.  
  705. void        SetMtrMax(mH, maxValue)
  706. MeterHandle        mH;
  707. long            maxValue;
  708.     {
  709.     GrafPtr        savePort;
  710.     
  711.     if (!ValidMeter(mH)) return;
  712.     GetPort(&savePort);
  713.     SetPort((**mH).meterOwner);
  714.     DrawSet(mH);     /* erases old pointer */
  715.     DrawNeedle(mH); /* ditto needle */
  716.     if (maxValue <= (**mH).meterMin)
  717.         {
  718.         (**mH).meterMax = (**mH).meterMin;
  719.         HiliteMeter(mH, 255); /* inactive - see IM 1-327 */
  720.         return;
  721.         }
  722.     (**mH).meterMax = maxValue;
  723.     if ((**mH).meterValue > maxValue)
  724.         (**mH).meterValue = maxValue;
  725.     SetDisplayDivisor(mH);
  726.     DrawSet(mH);
  727.     DrawNeedle(mH);
  728.     ShowValue(mH, FALSE);
  729.     SetPort(savePort);
  730.     } /* end SetMtrMax() */
  731.  
  732. long        GetMtrMax(mH)
  733. MeterHandle        mH;
  734.     {
  735.     
  736.     if (!ValidMeter(mH)) return(0L);
  737.     return((**mH).meterMax);
  738.     } /* end GetMtrMax() */
  739.  
  740. void        SetMRefCon(mH, refCon)
  741. MeterHandle        mH;
  742. long            refCon;
  743.     {
  744.     
  745.     if (!ValidMeter(mH)) return;
  746.     (**mH).meterRfCon = refCon;
  747.     } /* end SetMRefCon() */
  748.  
  749. long        GetMRefCon(mH)
  750. MeterHandle        mH;
  751.     {
  752.     
  753.     if (!ValidMeter(mH)) return(0L);
  754.     return((**mH).meterRfCon);
  755.     } /* end GetMRefCon() */
  756.  
  757. void        SetNeedleValue(mH, value)
  758. MeterHandle        mH;
  759. long            value;
  760.     {
  761.     GrafPtr        savePort;
  762.     
  763.     if (!ValidMeter(mH)) return;
  764.     if ((**mH).needleValue == value) return;
  765.     GetPort(&savePort);
  766.     SetPort((**mH).meterOwner);
  767.     DrawNeedle(mH); /* erases old needle */
  768.     /* pin value to range min:max */
  769.     if (value < (**mH).meterMin)
  770.         value = (**mH).meterMin;
  771.     else if (value > (**mH).meterMax)
  772.         value = (**mH).meterMax;
  773.     (**mH).needleValue = value;
  774.     DrawNeedle(mH);
  775.     ShowValue(mH, TRUE);
  776.     SetPort(savePort);
  777.     } /* end SetNeedleValue() */
  778.     
  779. long        GetNeedleValue(mH)
  780. MeterHandle        mH;
  781.     {
  782.     
  783.     if (!ValidMeter(mH)) return(0L);
  784.     return((**mH).needleValue);
  785.     } /* end GetNeedleValue() */
  786.  
  787. long        GetDisplayDivisor(mH)
  788. MeterHandle        mH;
  789.     {
  790.     
  791.     if (!ValidMeter(mH)) return(0L);
  792.     return((**mH).displayDivisor);
  793.     } /* end GetDisplayDivisor() */
  794.  
  795. Boolean        ValidMeter(mH)
  796. MeterHandle        mH;
  797.     {
  798.     
  799.     if (mH && (**mH).hasMeterID == HASMETER)
  800.         return(TRUE);
  801.     return(FALSE);
  802.     } /* end ValidMeter() */
  803.  
  804. int            GetMaxTitlePixels(mH)
  805. MeterHandle        mH;
  806.     {
  807.     
  808.     if (!ValidMeter(mH)) return(0);
  809.     return((**mH).meterRect.right - (**mH).meterRect.left - 4);
  810.     } /* end GetMaxTitlePixels() */
  811.  
  812. /* A meter, like a scroll bar, is very attached to its owning
  813. window. Only MeterSnapShot() will allow you to draw a meter
  814. in another grafPort. Set the port, and set the font in the port 
  815. to something appropriate for the meter title before the call. */
  816. void        MeterSnapshot(mH)
  817. MeterHandle        mH;
  818.     {
  819.     int            i;
  820.     Byte        mVis, mHilite;
  821.     
  822.     if (!ValidMeter(mH)) return;
  823.     mVis = (**mH).meterVis;
  824.     mHilite = (**mH).meterHilite;
  825.     (**mH).meterVis = TRUE;
  826.     (**mH).meterHilite = 0;
  827.     DrawMeterBackground(mH);
  828.     for (i = inUpButton; i <= inPageDown; ++i)
  829.         DrawTriangle(mH, i);
  830.     DrawNeedle(mH);
  831.     DrawSet(mH);
  832.     ShowValue(mH, TRUE);
  833.     (**mH).meterVis = mVis;
  834.     (**mH).meterHilite = mHilite;
  835.     } /* end MeterSnapshot() */
  836.  
  837. /* Functions used only in this file from here on */
  838.  
  839. /* Load up a table of sines to avoid going off on a tangent (sorry).
  840. If this table were entered by hand, <math.h> would not be needed. */
  841. void        InitMeters()
  842.     {
  843.     int        i;
  844.     double    x;
  845.     
  846.     for (i = 0; i <= 90; ++i)
  847.         {
  848.         x = ((double)i*PI)/180.0; /* convert degrees to radians */
  849.         sineTable[i] = (int)(sin(x)*1000.0);
  850.         }
  851.     } /* end InitMeters() */
  852.  
  853.  
  854. /* The real fun - create the meter background picture 
  855. and set up points and rects needed for drawing it. We could
  856. also have created the background in a drafting program and
  857. just scale it to the meter's rectangle, but this wouldn't be
  858. as precise or as easy to modify. */    
  859. void        CreateMeterBackground(mH)
  860. MeterHandle        mH;
  861.     {
  862.     int                i, j, k, h, v, posInc;
  863.     long            theSin, theCos;
  864.     long            radius, xo, yo, lowestTickH, lowestTickV;
  865.     Rect            boundRect, theClipRect, tempRect, innerCircle;
  866.     GrafPtr            savePort;
  867.     RgnHandle        clpRgn, dialRgn, innerTickRgn;
  868.     PenState        pState;
  869.     PicHandle        tempPicH;
  870.     PolyHandle        tempPoly;
  871.     Point            c, l, r; /* centre, left, right of polygons */
  872.     
  873.     GetPort(&savePort);
  874.     SetPort((**mH).meterOwner);
  875.     /* adjust meterRect - meter proportions are fixed, meterRect.bottom is ignored */
  876.     boundRect = (**mH).meterRect;
  877.     if (boundRect.right - boundRect.left < MINWIDTH)
  878.         boundRect.right = boundRect.left + MINWIDTH;
  879.     boundRect.bottom = boundRect.top + ((boundRect.right - boundRect.left)*TVHRATIO)/1000;
  880.     tempRect = boundRect;
  881.     boundRect.bottom += TITLEHEIGHT;
  882.     (**mH).meterRect = boundRect;
  883.     /* first adjust the clip; */
  884.     clpRgn = NewRgn();
  885.     dialRgn = NewRgn();
  886.     innerTickRgn = NewRgn();
  887.     GetClip(clpRgn);
  888.     /* then draw meter in rectangle starting at 0,0 for convenience */
  889.     OffsetRect(&boundRect,-boundRect.left, -boundRect.top);
  890.     OffsetRect(&tempRect,-tempRect.left, -tempRect.top);
  891.     theClipRect = boundRect;
  892.     theClipRect.right += 100;
  893.     theClipRect.bottom += 200;
  894.     ClipRect(&theClipRect);
  895.     GetPenState(&pState);
  896.     tempPicH = OpenPicture(&boundRect);
  897.         PenNormal();
  898.         /* draw black rounded rect */
  899.         FillRoundRect(&tempRect,tempRect.right/10, tempRect.right/10,black);
  900.         i = tempRect.bottom;
  901.         /*  and frame two white circles */
  902.         tempRect.bottom = tempRect.right; /* square it off */
  903.         InsetRect(&tempRect, tempRect.right/15, tempRect.right/15);
  904.         j = tempRect.bottom;
  905.         FillOval(&tempRect, white);
  906.         InsetRect(&tempRect, 3, 3);
  907.         radius = (tempRect.right - tempRect.left)/2;
  908.         (**mH).dialCentre.h = tempRect.left + radius;
  909.         (**mH).dialCentre.v = tempRect.top + radius;
  910.         FrameOval(&tempRect);
  911.         OpenRgn();
  912.             FrameOval(&tempRect);
  913.         CloseRgn(dialRgn);
  914.         k = radius - (radius*INNERPCT + 50L)/100L;
  915.         InsetRect(&tempRect, k, k);
  916.         innerCircle = tempRect;
  917.         OpenRgn();
  918.             FrameOval(&tempRect);
  919.         CloseRgn(innerTickRgn);
  920.         DiffRgn(dialRgn, innerTickRgn, dialRgn); /* that made a ring */
  921.         DisposeRgn(innerTickRgn);
  922.         /* white out rect for control triangles */
  923.         tempRect.top = j + j/20; /* j = bottom of larger dial circle */
  924.         tempRect.left = (**mH).dialCentre.h - radius;
  925.         tempRect.bottom = i - j/20; /* i = bottom of rounded rect */
  926.         tempRect.right = (**mH).dialCentre.h + radius;
  927.         i = j = tempRect.right - tempRect.left;
  928.         i = (i/4)*4;
  929.         tempRect.left += (j -i)/2;
  930.         tempRect.right = tempRect.left + i;
  931.         FillRect(&tempRect, white);
  932.         /* put lines between the control triangles */
  933.         MoveTo(tempRect.left + i/4, tempRect.top);
  934.         Line(0, tempRect.bottom - tempRect.top);
  935.         MoveTo(tempRect.left + i/2, tempRect.top);
  936.         Line(0, tempRect.bottom - tempRect.top);
  937.         MoveTo(tempRect.left + i/4 + i/2, tempRect.top);
  938.         Line(0, tempRect.bottom - tempRect.top);
  939.         tempRect.right = tempRect.left + i/4;
  940.         (**mH).pageDownRect = tempRect;
  941.         OffsetRect(&tempRect, i/4, 0);
  942.         (**mH).incDownRect = tempRect;
  943.         OffsetRect(&tempRect, i/4, 0);
  944.         (**mH).incUpRect = tempRect;
  945.         OffsetRect(&tempRect, i/4, 0);
  946.         (**mH).pageUpRect = tempRect;
  947.         SetClip(dialRgn);
  948.         /* dialRgn is the ring for the ticks */
  949.         PenSize(2,2);
  950.         /* Draw the major ticks around circumference of dial */
  951.         h = (**mH).dialCentre.h;
  952.         v = (**mH).dialCentre.v;
  953.         if (NUMTICKS >= 3)
  954.             {
  955.             posInc = 2500/(NUMTICKS - 1);
  956.             for (j = -1250; j <= 1250; j += posInc)
  957.                 {
  958.                 i = j/10;
  959.                 GetSinAndCos(i, &theSin, &theCos);
  960.                 xo = (radius*theSin + 500L)/1000L + h;
  961.                 yo = (-radius*theCos + 500L)/1000L + v;
  962.                 MoveTo(h - 1, v - 1);
  963.                 LineTo(xo - 1, yo - 1);
  964.                 }
  965.             }
  966.         /* and now the minor ticks */
  967.         PenSize(1,1);
  968.         if (NUMMINORTICKS >= 3)
  969.             {
  970.             posInc = 2500/(NUMMINORTICKS - 1);
  971.             for (j = -1250; j <= 1250; j += posInc)
  972.                 {
  973.                 i = j/10;
  974.                 /* determine sine and cosine to use */
  975.                 GetSinAndCos(i, &theSin, &theCos);
  976.                 xo = (radius*theSin + 500L)/1000L + h;
  977.                 yo = (-radius*theCos + 500L)/1000L + v;
  978.                 MoveTo(h - 1, v - 1);
  979.                 LineTo(xo - 1, yo - 1);
  980.                 }
  981.             }
  982.         
  983.         ClipRect(&theClipRect);
  984.         DisposeRgn(dialRgn);
  985.         /* White out centre - needed for clipboard copying. */
  986.         FillOval(&innerCircle, white);
  987.         /* put small black circle in centre of dial */
  988.         tempRect.top = (**mH).dialCentre.v - CENTRERADIUS;
  989.         tempRect.left = (**mH).dialCentre.h - CENTRERADIUS;
  990.         tempRect.bottom = (**mH).dialCentre.v + CENTRERADIUS;
  991.         tempRect.right = (**mH).dialCentre.h + CENTRERADIUS;
  992.         FillOval(&tempRect, black);
  993.         /* set up and draw displayRect just below dial centre */
  994.         lowestTickH = ((xo - h)*INNERPCT)/100L + h;
  995.         lowestTickV = ((yo - v)*INNERPCT)/100L + v;
  996.         tempRect.right = lowestTickH -2160/((**mH).meterRect.right - (**mH).meterRect.left);
  997.         tempRect.left = tempRect.right - 2*(tempRect.right - h);
  998.         tempRect.top = (int)lowestTickV;
  999.         tempRect.bottom = tempRect.top + 16;
  1000.         (**mH).displayRect = tempRect;
  1001.         AdjustDisplay(mH);
  1002.         tempRect = (**mH).displayRect;
  1003.         OffsetRect(&tempRect, -1, 0);
  1004.         FrameRect(&tempRect);
  1005.         MoveTo(tempRect.left + 1, tempRect.bottom);
  1006.         LineTo(tempRect.right, tempRect.bottom);
  1007.         MoveTo(tempRect.right, tempRect.top + 1);
  1008.         LineTo(tempRect.right, tempRect.bottom);
  1009.         InsetRect(&tempRect, 1, 1);
  1010.         i = (**mH).meterRect.left;
  1011.         j = (**mH).meterRect.top;
  1012.         OffsetRect(&tempRect, i,j);
  1013.         (**mH).displayRect = tempRect;
  1014.         SetDisplayDivisor(mH);
  1015.     ClosePicture();
  1016.     (**mH).backgroundH = tempPicH;
  1017.     /* define up and down arrows as triangular polygons */
  1018.     /* page up poly */
  1019.     tempRect = (**mH).pageUpRect;
  1020.     l.h = tempRect.left + (tempRect.right -tempRect.left)/8;
  1021.     r.h = tempRect.right - (tempRect.right -tempRect.left)/8;
  1022.     i = r.h - l.h; /* side length of triangle */
  1023.     i = (i/2)*2;
  1024.     r.h = l.h + i;
  1025.     j = (i*PGPOLYHEIGHT + 1000)/2000; /* vertical height, 1/2side * root3 */
  1026.     c.v = tempRect.top + (tempRect.bottom - tempRect.top - j)/2;
  1027.     c.h = tempRect.left + (tempRect.right - tempRect.left)/2;
  1028.     l.v = c.v + j;
  1029.     r.v = c.v + j;
  1030.     tempPoly = OpenPoly();
  1031.         MoveTo(c.h, c.v);
  1032.         LineTo(l.h, l.v);
  1033.         LineTo(r.h, r.v);
  1034.         LineTo(c.h, c.v);
  1035.     ClosePoly();
  1036.     (**mH).pageUpH = tempPoly;
  1037.     /* inc up poly */
  1038.     tempRect = (**mH).incUpRect;
  1039.     l.h = tempRect.left + (tempRect.right - tempRect.left)/4;
  1040.     r.h = tempRect.right - (tempRect.right - tempRect.left)/4;
  1041.     i = r.h - l.h; /* side length of triangle */
  1042.     i = (i/2)*2;
  1043.     r.h = l.h + i;
  1044.     j = (i*INCPOLYHEIGHT + 1000)/2000; /* vertical height, 1/2side * root3 */
  1045.     c.v = tempRect.top + (tempRect.bottom - tempRect.top - j)/2;
  1046.     c.h = tempRect.left + (tempRect.right - tempRect.left)/2;
  1047.     l.v = c.v + j;
  1048.     r.v = c.v + j;
  1049.     tempPoly = OpenPoly();
  1050.         MoveTo(c.h, c.v);
  1051.         LineTo(l.h, l.v);
  1052.         LineTo(r.h, r.v);
  1053.         LineTo(c.h, c.v);
  1054.     ClosePoly();
  1055.     (**mH).incUpH = tempPoly;
  1056.     /* inc down poly */
  1057.     tempRect = (**mH).incDownRect;
  1058.     l.h = tempRect.left + (tempRect.right - tempRect.left)/4;
  1059.     r.h = tempRect.right - (tempRect.right - tempRect.left)/4;
  1060.     i = r.h - l.h; /* side length of triangle */
  1061.     i = (i/2)*2;
  1062.     r.h = l.h + i;
  1063.     j = (i*INCPOLYHEIGHT + 1000)/2000; /* vertical height, 1/2side * root3 */
  1064.     c.v = tempRect.top + (tempRect.bottom - tempRect.top - j)/2 + j;
  1065.     c.h = tempRect.left + (tempRect.right - tempRect.left)/2;
  1066.     l.v = c.v - j;
  1067.     r.v = c.v - j;
  1068.     tempPoly = OpenPoly();
  1069.         MoveTo(c.h, c.v);
  1070.         LineTo(l.h, l.v);
  1071.         LineTo(r.h, r.v);
  1072.         LineTo(c.h, c.v);
  1073.     ClosePoly();
  1074.     (**mH).incDownH = tempPoly;
  1075.     /* page down poly */
  1076.     tempRect = (**mH).pageDownRect;
  1077.     l.h = tempRect.left + (tempRect.right -tempRect.left)/8;
  1078.     r.h = tempRect.right - (tempRect.right -tempRect.left)/8;
  1079.     i = r.h - l.h; /* side length of triangle */
  1080.     i = (i/2)*2;
  1081.     r.h = l.h + i;
  1082.     j = (i*PGPOLYHEIGHT + 1000)/2000; /* vertical height, 1/2side * root3 */
  1083.     c.v = tempRect.top + (tempRect.bottom - tempRect.top - j)/2 + j;
  1084.     c.h = tempRect.left + (tempRect.right - tempRect.left)/2;
  1085.     l.v = c.v - j;
  1086.     r.v = c.v - j;
  1087.     tempPoly = OpenPoly();
  1088.         MoveTo(c.h, c.v);
  1089.         LineTo(l.h, l.v);
  1090.         LineTo(r.h, r.v);
  1091.         LineTo(c.h, c.v);
  1092.     ClosePoly();
  1093.     (**mH).pageDownH = tempPoly;
  1094.     
  1095.     /* set up needle and set point parameters */
  1096.     radius = (radius*(INNERPCT-2L) + 50L)/100;
  1097.     (**mH).needleLong = radius;
  1098.     (**mH).setLong = radius;
  1099.     (**mH).needleShort = (lowestTickV - (**mH).dialCentre.v)/2;
  1100.     (**mH).setShort = (**mH).needleShort;
  1101.     (**mH).halfNeedleWidth = radius/HALFNEEDLERATIO;
  1102.     
  1103.     /* move rects and points to proper position */
  1104.     i = (**mH).meterRect.left;
  1105.     j = (**mH).meterRect.top;
  1106.     OffsetRect(&((**mH).incDownRect), i,j);
  1107.     OffsetRect(&((**mH).incUpRect), i,j);
  1108.     OffsetRect(&((**mH).pageDownRect), i,j);
  1109.     OffsetRect(&((**mH).pageUpRect), i,j);
  1110.     (**mH).dialCentre.h += i;
  1111.     (**mH).dialCentre.v += j;
  1112.     /* clean up on the way out */
  1113.     SetPenState(&pState);
  1114.     SetClip(clpRgn);
  1115.     DisposeRgn(clpRgn);
  1116.     SetPort(savePort);
  1117.     } /* end CreateMeterBackground() */
  1118.  
  1119. void        DrawMeterBackground(mH)
  1120. MeterHandle        mH;
  1121.     {
  1122.     PicHandle    tempPH = (**mH).backgroundH;
  1123.     Rect        tempRect;
  1124.     PenState    pState;
  1125.     
  1126.     GetPenState(&pState);
  1127.     PenNormal();
  1128.     tempRect = (**mH).meterRect;
  1129.     EraseRect(&tempRect);
  1130.     if ((**mH).meterVis)
  1131.         {
  1132.          if ((**mH).meterHilite != 255)
  1133.             DrawPicture(tempPH, &tempRect);
  1134.         else
  1135.             DrawInactiveMeter(mH);
  1136.         DrawMeterTitle(mH);
  1137.         }
  1138.     SetPenState(&pState);
  1139.     } /* end DrawMeterBackground() */
  1140.  
  1141. /* Title is centered and truncated if necessary, in whatever
  1142. font is current. Drawing is clipped to the intersection of
  1143. the title rect and the current clip region. */
  1144. void        DrawMeterTitle(mH)
  1145. MeterHandle        mH;
  1146.     {
  1147.     int                i,j, height;
  1148.     Rect            tempRect;
  1149.     char            title[32];
  1150.     FontInfo        tempInfo;
  1151.     PenState        pState;
  1152.     RgnHandle        oldClipRgn, titleClipRgn;
  1153.     ControlHandle    cHH, cHV;
  1154.     char            *mTPtr = (**mH).title;
  1155.     
  1156.     GetFontInfo(&tempInfo);
  1157.     height = tempInfo.ascent + tempInfo.descent + tempInfo.leading;
  1158.     /* copy title - text must not be referenced by
  1159.     an unlocked handle when calling DrawString() */
  1160.     for (i = 0; i < 31; ++i)
  1161.         title[i] = mTPtr[i];
  1162.     /* draw shadowed title box, effective height = 
  1163.     TITLEHEIGHT - 4, just below black rounded rect */
  1164.     tempRect.left = (**mH).meterRect.left + 2;
  1165.     tempRect.right = (**mH).meterRect.right - 2;
  1166.     tempRect.bottom = (**mH).meterRect.bottom - 2;
  1167.     tempRect.top = tempRect.bottom - TITLEHEIGHT + 4;
  1168.     FrameRect(&tempRect);
  1169.     MoveTo(tempRect.left + 1, tempRect.bottom);
  1170.     LineTo(tempRect.right, tempRect.bottom);
  1171.     MoveTo(tempRect.right, tempRect.top + 1);
  1172.     LineTo(tempRect.right, tempRect.bottom);
  1173.     i = StringWidth(title);
  1174.     j = tempRect.right - tempRect.left;
  1175.     if ((j - i) < 2) /* title too long - truncate */
  1176.         {
  1177.         while (StringWidth(title) > j - 2)
  1178.             title[0] -= 1;
  1179.         i = StringWidth(title);
  1180.         }
  1181.     GetFontInfo(&tempInfo);
  1182.     height = tempInfo.ascent + tempInfo.descent + tempInfo.leading;
  1183.     MoveTo(tempRect.left + (j - i)/2, 
  1184.         tempRect.top + (tempRect.bottom - tempRect.top)/2 - height/2 + tempInfo.ascent);
  1185.     oldClipRgn = NewRgn();
  1186.     titleClipRgn = NewRgn();
  1187.     GetClip(oldClipRgn);
  1188.     RectRgn(titleClipRgn, &tempRect);
  1189.     SectRgn(titleClipRgn, oldClipRgn, titleClipRgn);
  1190.     SetClip(titleClipRgn);
  1191.     GetPenState(&pState);
  1192.     PenNormal();
  1193.     
  1194.     DrawString(title);
  1195.     
  1196.     SetPenState(&pState);
  1197.     SetClip(oldClipRgn);
  1198.     DisposeRgn(oldClipRgn);
  1199.     DisposeRgn(titleClipRgn);
  1200.     } /* end DrawMeterTitle() */
  1201.  
  1202. /* The four triangles (two small, two large)
  1203. correspond to the inc and page parts of a 
  1204. scroll bar. */    
  1205. void        DrawTriangle(mH, partCode)
  1206. MeterHandle        mH;
  1207. int                partCode;
  1208.     {
  1209.     PenState    pState;
  1210.     PolyHandle    tempPoly;
  1211.     
  1212.     if ((**mH).meterHilite == 255) return;
  1213.     if (partCode == inUpButton)
  1214.         tempPoly = (**mH).incUpH;
  1215.     else if (partCode == inDownButton)
  1216.         tempPoly = (**mH).incDownH;
  1217.     else if (partCode == inPageUp)
  1218.         tempPoly = (**mH).pageUpH;
  1219.     else if (partCode == inPageDown)
  1220.         tempPoly = (**mH).pageDownH;
  1221.     else 
  1222.         return;
  1223.     GetPenState(&pState);
  1224.     PenNormal();
  1225.     OffsetPoly(tempPoly, (**mH).meterRect.left, (**mH).meterRect.top);
  1226.     if (partCode == (**mH).meterHilite)
  1227.         {
  1228.         OffsetPoly(tempPoly, -1, -1);
  1229.         FillPoly(tempPoly, black);
  1230.         }
  1231.     else
  1232.         {/* neat way to shadow ANY polygon */
  1233.         FillPoly(tempPoly, black);
  1234.         FramePoly(tempPoly);
  1235.         OffsetPoly(tempPoly, -1, -1);
  1236.         ErasePoly(tempPoly);
  1237.         FramePoly(tempPoly);
  1238.         }
  1239.     OffsetPoly(tempPoly, -(**mH).meterRect.left + 1, -(**mH).meterRect.top + 1);
  1240.     SetPenState(&pState);
  1241.     } /* end DrawTriangle() */
  1242.     
  1243. void        DrawNeedle(mH)
  1244. MeterHandle        mH;
  1245.     {
  1246.     long        l1, l2, l3, xa, xb, xc, xd;
  1247.     long        theSin, theCos, ya, yb, yc, yd;
  1248.     int            position;
  1249.     int            h = (**mH).dialCentre.h;
  1250.     int            v = (**mH).dialCentre.v;
  1251.     PenState    pState;
  1252.     PolyHandle    needlePoly;
  1253.     
  1254.     if (!((**mH).meterVis) || (**mH).meterHilite == 255) return;
  1255.     GetPenState(&pState);
  1256.     PenMode(patXor);
  1257.     PenPat(black);
  1258.     PenSize(1,1);
  1259.     
  1260.     /* Determine position of needle based on value. Position
  1261.     is the angle in degrees measured from 0 = straight up,
  1262.     increasing clockwise, range -125:+125 */
  1263.     position = (((**mH).needleValue - (**mH).meterMin)*250L)
  1264.                 /((**mH).meterMax - (**mH).meterMin) - 125L;
  1265.     if (position < -125)
  1266.         position = -125;
  1267.     if (position > 125)
  1268.         position = 125;
  1269.     /* Determine sine and cosine to use. */
  1270.     GetSinAndCos(position, &theSin, &theCos);
  1271.     /* Calculate four vertices of needle indicator by
  1272.     changing from polar to rectangular coord's and
  1273.     then translating over to dial centre. */
  1274.     l1 = (**mH).needleLong;
  1275.     l2 = (**mH).needleShort;
  1276.     l3 = (**mH).halfNeedleWidth;
  1277.     xa = (-l2*theSin + 500L)/1000L + h;
  1278.     ya = (l2*theCos + 500L)/1000L + v;
  1279.     xb = (l1*theSin + 500L)/1000L + h;
  1280.     yb = (-l1*theCos + 500L)/1000L + v;
  1281.     xc = (-l3*theCos + 500L)/1000L + h;
  1282.     yc = (-l3*theSin + 500L)/1000L + v;
  1283.     xd = -xc + 2*h;
  1284.     yd = -yc + 2*v;
  1285.     
  1286.     needlePoly = OpenPoly();
  1287.     MoveTo(xa, ya);
  1288.     LineTo(xc, yc);
  1289.     LineTo(xb, yb);
  1290.     LineTo(xd, yd);
  1291.     LineTo(xa, ya);
  1292.     ClosePoly();
  1293.     PaintPoly(needlePoly);
  1294.     KillPoly(needlePoly);
  1295.     
  1296.     SetPenState(&pState);
  1297.     } /* end DrawNeedle() */
  1298.     
  1299. void        DrawSet(mH)
  1300. MeterHandle        mH;
  1301.     {
  1302.     long        l1, l2, l3, xa, xb;
  1303.     long        theSin, theCos, ya, yb;
  1304.     int            position;
  1305.     int            thePenSize = (**mH).halfSetWidth*2;
  1306.     int            h = (**mH).dialCentre.h;
  1307.     int            v = (**mH).dialCentre.v;
  1308.     PenState    pState;
  1309.     
  1310.     if (!((**mH).meterVis) || (**mH).meterHilite == 255) return;
  1311.     GetPenState(&pState);
  1312.     PenMode(patXor);
  1313.     PenPat(black);
  1314.     PenSize(thePenSize,thePenSize);
  1315.     
  1316.     /* determine position of set indicator from value */
  1317.     position = (((**mH).meterValue - (**mH).meterMin)*250L)
  1318.                 /((**mH).meterMax - (**mH).meterMin) - 125L;
  1319.     if (position < -125)
  1320.         position = -125;
  1321.     if (position > 125)
  1322.         position = 125;
  1323.     
  1324.     /* determine sine and cosine to use */
  1325.     GetSinAndCos(position, &theSin, &theCos);
  1326.         
  1327.     /* calculate two vertices of position indicator */
  1328.     l1 = (**mH).setLong;
  1329.     l2 = (**mH).setShort;
  1330.     l3 = (**mH).halfSetWidth;
  1331.     xa = (-l2*theSin + 500L)/1000L - HALFSETWIDTH + h;
  1332.     ya = (l2*theCos + 500L)/1000L - HALFSETWIDTH + v;
  1333.     xb = (l1*theSin + 500L)/1000L - HALFSETWIDTH + h;
  1334.     yb = (-l1*theCos + 500L)/1000L - HALFSETWIDTH + v;
  1335.     
  1336.     MoveTo(xa, ya);
  1337.     LineTo(xb, yb);
  1338.     
  1339.     SetPenState(&pState);
  1340.     } /* end DrawSet() */
  1341.     
  1342. void        ShowValue(mH, needle)
  1343. MeterHandle        mH;
  1344. Boolean            needle;
  1345.     {
  1346.     int            i;
  1347.     int            oldFont, oldSize;
  1348.     Style        oldStyle;
  1349.     PenState    pState;
  1350.     long        theValue;
  1351.     Rect        tempRect;
  1352.     char        pNumStr[16]; /* pascal */
  1353.     
  1354.     if (!((**mH).meterVis) || (**mH).meterHilite == 255) return;
  1355.     GetPenState(&pState);
  1356.     PenNormal();
  1357.     oldFont = thePort->txFont;
  1358.     oldSize = thePort->txSize;
  1359.     oldStyle = thePort->txFace;
  1360.     TextFont(geneva);
  1361.     TextSize(12);
  1362.     TextFace(' ');
  1363.     if (needle)
  1364.         theValue = (**mH).needleValue;
  1365.     else
  1366.         theValue = (**mH).meterValue;
  1367.     /* Truncate value to fit display box. */
  1368.     if ((**mH).displayDivisor > 1L)
  1369.         theValue /= (**mH).displayDivisor;
  1370.     tempRect = (**mH).displayRect;
  1371.     NumToString(theValue, pNumStr);
  1372.     i = StringWidth(pNumStr);
  1373.     EraseRect(&tempRect);
  1374.     MoveTo(tempRect.right - i- 4, 
  1375.         tempRect.top + (tempRect.bottom - tempRect.top)/2 + TEXTVOFFSET);
  1376.     DrawString(pNumStr);
  1377.     TextFont(oldFont);
  1378.     TextSize(oldSize);
  1379.     TextFace(oldStyle);
  1380.     SetPenState(&pState);
  1381.     } /* end ShowValue() */
  1382.  
  1383. void        DrawInactiveMeter(mH)
  1384. MeterHandle        mH;
  1385.     {
  1386.     Rect        tempRect;
  1387.     
  1388.     tempRect = (**mH).meterRect;
  1389.     tempRect.bottom -= TITLEHEIGHT;
  1390.     FrameRoundRect(&tempRect,tempRect.right/10, tempRect.bottom/10);
  1391.     } /* end DrawInactiveMeter() */
  1392.  
  1393. /* Ensure that digital display shows at least MINDIGITS and at most MAXDIGITS
  1394.  plus sign - called only during creation of background meter picture. */
  1395. void        AdjustDisplay(mH)
  1396. MeterHandle        mH;
  1397.     {
  1398.     int        oldFont, oldSize;
  1399.     Style    oldStyle;
  1400.     int        displayWidth = (**mH).displayRect.right - (**mH).displayRect.left;
  1401.     int        minusW, num4W;
  1402.     int        numDisplayDigits, wantedWidth, halfWidthDiff;
  1403.     
  1404.     oldFont = thePort->txFont;
  1405.     oldSize = thePort->txSize;
  1406.     oldStyle = thePort->txFace;
  1407.     TextFont(geneva);
  1408.     TextSize(12);
  1409.     TextFace(' ');
  1410.     minusW = CharWidth('-');
  1411.     num4W = CharWidth('4');
  1412.     numDisplayDigits = (displayWidth - minusW - 8)/num4W;
  1413.     if (numDisplayDigits < MINDIGITS) /* too small */
  1414.         {
  1415.         wantedWidth = 8 + minusW + num4W*MINDIGITS;
  1416.         halfWidthDiff = (wantedWidth - displayWidth + 1)/2;
  1417.         (**mH).displayRect.left -= halfWidthDiff;
  1418.         (**mH).displayRect.right += halfWidthDiff;
  1419.         }
  1420.     else if (numDisplayDigits > MAXDIGITS) /* too big */
  1421.         {
  1422.         wantedWidth = 8 + minusW + num4W*MAXDIGITS;
  1423.         halfWidthDiff = (displayWidth - wantedWidth -1)/2;
  1424.         (**mH).displayRect.left += halfWidthDiff;
  1425.         (**mH).displayRect.right -= halfWidthDiff;
  1426.         }
  1427.     TextFont(oldFont);
  1428.     TextSize(oldSize);
  1429.     TextFace(oldStyle);
  1430.     } /* end AdjustDisplay() */
  1431.  
  1432. /* Set displayDivisor to appropriate power of ten so that as many digits
  1433. as possible are shown in the display. Called when creating meter and
  1434. also when setting meter max or min */
  1435. void        SetDisplayDivisor(mH)
  1436. MeterHandle        mH;
  1437.     {
  1438.     int        oldFont, oldSize;
  1439.     Style    oldStyle;
  1440.     int        displayWidth = (**mH).displayRect.right - (**mH).displayRect.left + 2;
  1441.     int        minusW, num4W, numDisplayDigits, wantToDisplay, power;
  1442.     long    absMin, absMax;
  1443.     char    pNumStr[16];    /* pascal */
  1444.     
  1445.     oldFont = thePort->txFont;
  1446.     oldSize = thePort->txSize;
  1447.     oldStyle = thePort->txFace;
  1448.     TextFont(geneva);
  1449.     TextSize(12);
  1450.     TextFace(' ');
  1451.     minusW = CharWidth('-');
  1452.     num4W = CharWidth('4');
  1453.     numDisplayDigits = (displayWidth - minusW - 8)/num4W;
  1454.     absMin = ((**mH).meterMin < 0) ? - (**mH).meterMin : (**mH).meterMin;
  1455.     absMax = ((**mH).meterMax < 0) ? - (**mH).meterMax : (**mH).meterMax;
  1456.     if (absMin > absMax)
  1457.         absMax = absMin;
  1458.     NumToString(absMax, pNumStr);
  1459.     wantToDisplay = (int)(pNumStr[0]);
  1460.     if (wantToDisplay > numDisplayDigits) /* too many, must scale value down */
  1461.         {
  1462.         /* divisor = 10 raised to power (wantToDisplay - numDisplayDigits) */
  1463.         power = wantToDisplay - numDisplayDigits;
  1464.         (**mH).displayDivisor = 10L;
  1465.         while (--power > 0)
  1466.             (**mH).displayDivisor *= 10L;
  1467.         }
  1468.     else /* no scaling necessary */
  1469.         (**mH).displayDivisor = 1L;
  1470.     TextFont(oldFont);
  1471.     TextSize(oldSize);
  1472.     TextFace(oldStyle);
  1473.     } /* end SetDisplayDivisor() */
  1474.  
  1475. /* Position is interpreted as an angle in degrees. The
  1476. values are retrieved from sineTable[], which is an array
  1477. of integers recording sines from 0 to 90 degrees, scaled
  1478. up by 1000. */
  1479. void        GetSinAndCos(position, theSin, theCos)
  1480. int        position;
  1481. long    *theSin, *theCos;
  1482.     {
  1483.     
  1484.     if (position < -125 || position > 125)
  1485.         {
  1486.         *theSin = 0L;
  1487.         *theCos = 0L;
  1488.         return;
  1489.         }
  1490.     else if (-125 <= position && position <= -90)
  1491.         {
  1492.         *theSin = -sineTable[position + 180];
  1493.         *theCos = -sineTable[-position - 90];
  1494.         }
  1495.     else if (-90 < position && position <= 0)
  1496.         {
  1497.         *theSin = -sineTable[-position];
  1498.         *theCos = sineTable[position + 90];
  1499.         }
  1500.     else if (0 < position && position <= 90)
  1501.         {
  1502.         *theSin = sineTable[position];
  1503.         *theCos = sineTable[90 - position];
  1504.         }
  1505.     else /* 90:125 */
  1506.         {
  1507.         *theSin = sineTable[180 - position];
  1508.         *theCos = -sineTable[position - 90];
  1509.         }
  1510.     } /* end GetSinAndCos() */